<!--
 * @Author       : 梁嘉琳
 * @Date         : 2022-05-27 11:11:29
 * @LastEditors  : 梁嘉琳
 * @LastEditTime : 2022-06-30 10:23:54
 * @FilePath     : \my\examples\optStructure.html
-->
<!DOCTYPE html>
<html lang="zh">
	<head>
		<title>threejs学习</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
		<link rel="icon" href="../favicon.ico" />
		<link type="text/css" rel="stylesheet" href="../public/main.css" />
	</head>
	<body>
		<ul>
			<li><h1>不删除重复点求线面组</h1></li>
			<li><h1>数据返回格式调整：参照initGeometry</h1></li>
		</ul>
		<canvas id="fireflx"></canvas>
		<script type="importmap">
			{
				"imports": {
					"three": "../node_modules/three/build/three.module.js",
					"three/": "../node_modules/three/"
				}
			}
		</script>
		<script type="module">
			import * as THREE from 'three';
			console.time('end');
			const cos15 = Math.cos(Math.PI / 12);
			function getObj({ point: _point, line: _line, face: _face }) {
				// console.log(_point, _line, _face);
				const faceGroup = [];

				function initArr(arr, length) {
					const temporaryArr = [];
					for (let i = 0, n = arr.length; i < n; i += length) {
						if (length === 3) temporaryArr.push([arr[i], arr[i + 1], arr[i + 2]]);
						else temporaryArr.push([arr[i], arr[i + 1]]);
					}
					return temporaryArr;
				}

				const point = initArr(_point, 3),
					line = initArr(_line, 2),
					face = initArr(_face, 3);

				// console.log(point, line, face);

				let $point, $line, $face;

				class BaseStructrue {
					constructor(v, i) {
						this.label = i;
						this.value = v;
						this.key = v.toString();
					}
				}

				class Ponit extends BaseStructrue {
					constructor(v, i) {
						super(v, i);
						this.lines = [];
						this.faces = [];
					}
				}
				class Line extends BaseStructrue {
					constructor(v, i) {
						super(v, i);
						this.faces = [];
					}
				}
				class Face extends BaseStructrue {
					constructor(v, i) {
						super(v, i);
						this.lines = [];
					}
				}

				$point = point.map((v, i) => new Ponit(v, i));
				$line = line.map((v, i) => new Line(v, i));
				$face = face.map((v, i) => new Face(v, i));
				// console.log($point, $line, $face);

				for (let i = 0, n = $line.length; i < n; i++) {
					const v = $line[i];
					const [p1, p2] = v.value; //对每条线进行循环
					$point[p1].lines.push(v.label);
					$point[p2].lines.push(v.label);
					$face.forEach((fv) => {
						if (fv.value.includes(p1) && fv.value.includes(p2)) {
							v.faces.push(fv.label);
							fv.lines.push(v.label);
						}
					});
					if (v.faces.length !== 2) return;
					let cos;
					{
						const [x1, y1, z1] = $point[p1].value;
						const [x2, y2, z2] = $point[p2].value;
						const [x3, y3, z3] =
							$point[$face[v.faces[0]].value.filter((fp) => !v.value.includes(fp))[0]].value;
						const [x4, y4, z4] =
							$point[$face[v.faces[1]].value.filter((fp) => !v.value.includes(fp))[0]].value;

						const xv1 = x3 - x1;
						const yv1 = y3 - y1;
						const zv1 = z3 - z1;
						const xv2 = x2 - x1;
						const yv2 = y2 - y1;
						const zv2 = z2 - z1;
						const xv3 = x4 - x1;
						const yv3 = y4 - y1;
						const zv3 = z4 - z1;

						const n1 = yv1 * zv2 - zv1 * yv2;
						const n2 = zv1 * xv2 - xv1 * zv2;
						const n3 = xv1 * yv2 - yv1 * xv2;
						const n4 = yv1 * zv3 - zv1 * yv3;
						const n5 = zv1 * xv3 - xv1 * zv3;
						const n6 = xv1 * yv3 - yv1 * xv3;

						cos = (n1 * n4 + n2 * n5 + n3 * n6) / (Math.hypot(n1, n2, n3) * Math.hypot(n4, n5, n6));
					}

					if (cos > cos15) faceGroup.push(v.faces);
				}

				$face.forEach((v) => {
					const [p1, p2, p3] = v.value; //对每个面进行循环
					$point[p1].faces.push(v.label);
					$point[p2].faces.push(v.label);
					$point[p3].faces.push(v.label);
				});
				function getLightData(arr) {
					const res = [];
					for (let i = 0, n = arr.length; i < n; i++) {
						if (!arr[i]) continue;
						res.push(arr[i]);
						for (let j = i + 1; j < n; j++) {
							if (!arr[j]) continue;
							const len = res[res.length - 1].length;
							const s = new Set(res[res.length - 1].concat(arr[j]));
							if (len + arr[j].length !== s.size) {
								res[res.length - 1] = Array.from(s);
								arr[j] = null;
							}
						}
					}
					return res;
				}
				const res = getLightData(faceGroup);
				console.log('面组', res);

				const lineGroup = [];
				$line.forEach((v) => {
					const [f1, f2] = v.faces;
					res.forEach((fg) => {
						if (
							((fg.includes(f1) && !fg.includes(f2)) || (fg.includes(f2) && !fg.includes(f1))) &&
							!lineGroup.includes(v.label)
						)
							lineGroup.push(v.label);
					});
				});
				console.log('棱', lineGroup);

				function getIntersection(arr1, arr2) {
					return arr1.filter((v) => arr2.includes(v));
				}

				function getLineCos(p1, p2, p3) {
					const [x1, y1, z1] = $point[p2].value;
					const [x2, y2, z2] = $point[p1].value;
					const [x3, y3, z3] = $point[p3].value;
					const nx1 = x1 - x2;
					const ny1 = y1 - y2;
					const nz1 = z1 - z2;
					const nx2 = x3 - x2;
					const ny2 = y3 - y2;
					const nz2 = z3 - z2;
					return (
						(nx1 * nx2 + ny1 * ny2 + nz1 * nz2) / (Math.hypot(nx1, ny1, nz1) * Math.hypot(nx2, ny2, nz2))
					);
				}
				function getVectorPoint(arr1, arr2) {
					const interSection = getIntersection(arr1, arr2)[0];
					const unionSet = new Set(arr1.concat(arr2));
					const union = Array.from(unionSet).filter((v) => v !== interSection);
					union.unshift(interSection);
					return union;
				}
				const temLineRes = [];
				lineGroup.forEach((v) => {
					lineGroup.forEach((v2) => {
						if (v === v2) return;
						const l1 = $line[v].value;
						const l2 = $line[v2].value;
						if (getIntersection(l1, l2).length === 0) return;
						const [p1, p2, p3] = getVectorPoint(l1, l2);
						const cos = getLineCos(p1, p2, p3);
						if (Math.abs(cos) > cos15) temLineRes.push([v, v2]);
					});
				});
				// console.log(temLineRes);
				// console.error('圆柱低于25个sagments无效');
				console.log('棱组', getLightData(temLineRes));
			}

			const initVertices = (vertices) => {
				// console.log(vertices);
				//点的信息
				const PointMap = new Map();
				const numVertices = vertices.count;
				// 三个一组为面 ，根据点索引找到面组成的点
				const index = [];
				for (let i = 0; i < numVertices; i++) {
					let x = Number(vertices.getX(i).toFixed(10));
					let y = Number(vertices.getY(i).toFixed(10));
					let z = Number(vertices.getZ(i).toFixed(10));
					x = x === 0 ? 0 : x;
					y = y === 0 ? 0 : y;
					z = z === 0 ? 0 : z;
					const p = new THREE.Vector3(x, y, z);
					const p_str = JSON.stringify(p);

					if (!PointMap.has(p_str)) {
						PointMap.set(p_str, PointMap.size);
					}
					index.push(PointMap.get(p_str));
				}

				//生成线的结构
				const lineSet = new Set();
				for (let i = 0; i < index.length; i += 3) {
					const line1 = JSON.stringify({ p1: index[i], p2: index[i + 1] });
					const line2 = JSON.stringify({ p1: index[i], p2: index[i + 2] });
					const line3 = JSON.stringify({ p1: index[i + 1], p2: index[i + 2] });
					const line1f = JSON.stringify({ p1: index[i + 1], p2: index[i] });
					const line2f = JSON.stringify({ p1: index[i + 2], p2: index[i] });
					const line3f = JSON.stringify({ p1: index[i + 2], p2: index[i + 1] });
					!(lineSet.has(line1) || lineSet.has(line1f)) ? lineSet.add(line1) : '';
					!(lineSet.has(line2) || lineSet.has(line2f)) ? lineSet.add(line2) : '';
					!(lineSet.has(line3) || lineSet.has(line3f)) ? lineSet.add(line3) : '';
				}

				//转成数组后可以方便根据index信息索引 点数据
				//	{point: arr , line : lineSet, face: index}  或者都转成数组结构
				const lineSetArray = Array.from(lineSet);
				const lineArr = [];
				for (let i = 0; i < lineSetArray.length; i++) {
					const p1 = JSON.parse(lineSetArray[i]).p1;
					const p2 = JSON.parse(lineSetArray[i]).p2;
					lineArr.push(p1, p2);
				}
				// mesh对应的原始数组 ，转变为index，根据index索引原始数组信息
				// const verticesArr = Array.from(vertices.array);

				const pointSetArray = Array.from(PointMap.keys());

				const pointArray = [];
				for (let i = 0; i < pointSetArray.length; i++) {
					const p_str = pointSetArray[i];
					const p = JSON.parse(p_str);
					pointArray.push(p.x, p.y, p.z);
				}

				return { point: pointArray, line: lineArr, face: index };
			};
			const data = {
				width: 1,
				height: 1,
				depth: 1,
				widthSegments: 1,
				heightSegments: 1,
				depthSegments: 1,
			};

			const geo = new THREE.BoxGeometry(
				data.width,
				data.height,
				data.depth,
				data.widthSegments,
				data.heightSegments,
				data.depthSegments
			);
			// const geo = new THREE.CylinderGeometry(5, 5, 10, 25);
			// console.log(geo);
			const geoNew = geo.toNonIndexed();
			const geoObj = initVertices(geoNew.getAttribute('position'));
			console.log(getObj(geoObj));
			console.timeEnd('end');
		</script>
	</body>
</html>
